/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.merak.core.model;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingletonHolder<T> {

    //~ Attributes ///////////////////////////////////////////////////////////////
    //****************************************************************************
    private T        instance   = null;
    private Class<T> clazz      = null;
    private boolean  instancing = false;

    //~ Constructors /////////////////////////////////////////////////////////////
    //****************************************************************************
    public SingletonHolder(Class<T> clazz) {

        this.clazz = clazz;

    }

    //~ Methods //////////////////////////////////////////////////////////////////
    //****************************************************************************
    private void createInstance() {

        // Check "Singleton Lock" in same thread
        if (this.instancing) throw new Error (
        	"Singleton DeadLock detected: getInstance() called recursively"
        );

        // Create the singleton instance
        try {
            // Ensure the default constructor is accessible
            Constructor<T> c = this.clazz.getDeclaredConstructor();
            c.setAccessible(true);
            // Call default constructor
            this.instancing = true;
            this.instance   = c.newInstance();
        }
        catch (InvocationTargetException e) {
        	Throwable cause = (e.getCause() != null) ? e.getCause() : e;
        	if (cause instanceof RuntimeException) throw (RuntimeException)cause;
        	if (cause instanceof Error           ) throw (Error)cause;
        	throw new Error("Error instancing Singleton of " + this.clazz,cause);
        }
        catch (RuntimeException e) { throw e; }
        catch (Error e           ) { throw e; }
        catch (Throwable t       ) {
        	throw new Error("Error instancing Singleton of " + this.clazz,t);
        }
        finally {
            /* Always set instancing to false, as it might
             * alert false dead locks in the future */
            this.instancing = false;
        }

    }

    //****************************************************************************
    public synchronized T getInstance() {

    	if (this.instance == null) {
    		this.createInstance();
    	}
        return this.instance;

    }
    //****************************************************************************
    public synchronized void deleteInstance() {

        this.instance = null;

    }
    //****************************************************************************

}
